<?php

Raise::load('core.RaiseStream');
Raise::load('core.RaiseDns');

/**
 * RaiseHttpStream class
 * represents a HTTP stream
 *
 * @author Sam-Mauris Yong <hellclanner at live dot com>
 * @license http://www.opensource.org/licenses/bsd-license New BSD License
 * @package Raise.Core.IO
 * @since 1.2
 */
class RaiseHttpStream extends RaiseStream {

    /**
     * Create a RaiseHttpStream with connection to a client on a port
     * @param string|IpAddress $address The hostname or IP address of the remote client to connect to
     * @param integer $port (optional) The port number to communicate to. Defaults to port 80 for HTTP.
     */
    public function __construct($address, $port = 80){
        $address = RaiseDns::getAddressByName($address);
        $this->handle = @socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
        if($this->handle){
            $ok = @socket_connect($this->handle, $address, $port);
            if(!$ok){
                throw new RaiseRemoteConnectionException('RaiseHttpStream failed to connect to remote host at ' . $address . ':' . $port . '.');
            }
        }else{
            throw new RaiseRemoteConnectionException('RaiseHttpStream failed to create socket.');
        }
    }

    /**
     * Close and disconnect the socket connection
     */
    public function close() {
        socket_close($this->handle);
    }

    /**
     * getLength operation is not available on RaiseHttpStream
     * @ignore
     */
    public function length() {
        throw new RaiseInvalidRequestException('getLength() is not available in the context of RaiseHttpStream');
    }

    /**
     * Query whether the RaiseHttpStream can be read from.
     * Will always return true for RaiseHttpStream
     * @return boolean
     */
    public function isReadable() {
        return true;
    }

    /**
     * Query whether the RaiseHttpStream can be seek into.
     * Will always return false for RaiseHttpStream
     * @return boolean
     */
    public function isSeekable() {
        return false;
    }

    /**
     * Query whether the RaiseHttpStream can be written to.
     * Will always return true for RaiseHttpStream
     * @return boolean
     */
    public function isWriteable() {
        return true;
    }

    /**
     * Read data from the RaiseHttpStream
     * @param int $length The number of byte to read
     * @return string The string buffer returned. On error or client disconnection,
     * FALSE will be returned. If there is no more data to read, the operation will return an empty string "".
     */
    public function read($length) {
        $buffer = '';
        $ok = socket_recv($this->handle, $buffer, $length, 0);
        if($ok === false){
            throw new RaiseNetworkOperationFailedException('Failure to read data from RaiseHttpStream');
        }
        return $buffer;
    }

    /**
     * readByte operation is not available on RaiseHttpStream
     * @ignore
     */
    public function readByte() {
        throw new RaiseInvalidRequestException('readByte() is not available in the context of RaiseHttpStream');
    }

    /**
     * seek operation is not available on RaiseHttpStream
     * @ignore
     */
    public function seek($position) {
        throw new RaiseInvalidRequestException('seek() is not available in the context of RaiseHttpStream');
    }

    /**
     * Write to the RaiseHttpStream
     * @param string $str The string buffer to send
     * @return int The number of bytes successfully sent
     */
    public function write($str) {
        $buff = socket_write($this->handle, $str, strlen($str));
        if($buff === false){
            throw new RaiseNetworkOperationFailedException('Failure to write data to RaiseHttpStream');
        }
        return $buff;
    }

    /**
     * writeByte operation is not available on RaiseHttpStream
     * @ignore
     */
    public function writeByte($i) {
        throw new RaiseInvalidRequestException('writeByte() is not available in the context of RaiseHttpStream');
    }

}

